/**
 * 
 * 
 * @author Ryan Thomas <s548169@sbuniv.edu>
 * @version 0.7
 * Revision information: 
 * Version 0.6 removed anything related to encryption
 * Version 0.7 set Exception output to only display when debug mode is on
 */

package MultiUserChatServer;

//Begin Imports
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
//End Imports

public class ClientThread extends Thread{
    
    private boolean control;
    private Socket clientSocket;
    private ThreadLinkedList threads;
    private String userName;
    private final String[] reqIDtable= {"ENCRPT","PLNTXT","TERMCL", "BANUSR","SVRKEY"};
    private final String[] setIDtable= {"INIKEY","CLTEXP","CLTKEY","USRNAM"};
    private DataOutputStream outToClient;
    private DebugHandler debug;
    
    public ClientThread(Socket clientSocket, ThreadLinkedList buffers, DebugHandler debug){
        control = true;
        this.clientSocket = clientSocket;
        this.threads = buffers;
        userName = "";
        this.debug = debug;
        try {
            outToClient = new DataOutputStream(clientSocket.getOutputStream());
        } catch (IOException ex) {
            if(debug.get()){
                System.out.println(ex.getMessage());
            }
        }
    }
    
    @Override
    public void run(){
        try {
            String input;
            BufferedReader inFromUser = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
            while (control){
                input = inFromUser.readLine();//Read from User
                //The Following is option code handling, This handles requests made to the server for specific items
                //It also handles setting parameters on the server
                if(input.startsWith("OPT:")){
                    if(input.length()>10){
                            String revised = input.substring(4, input.length());
                            String optCode = revised.substring(0, 3);
                            String paramCode = revised.substring(4, 10);
                            String codeData="";
                            if(revised.length()>11){
                                codeData = revised.substring(12,revised.length());
                            }
                            if(optCode.equalsIgnoreCase("REQ")){
                                int requestID = this.optionParse(optCode, paramCode);
                                switch(requestID){
                                    case 3: outToClient.writeBytes("SVR:RPLY:TERM-OKAY\n");
                                            control = false;
                                            break;
                                        
                                }
                            }
                            else if(optCode.equalsIgnoreCase("SET")){
                                int setID = this.optionParse("SET", paramCode);
                                switch(setID){
                                    case 4: userName = codeData;
                                            outToClient.writeBytes("SVR:RPLY:SET-OKAY\n");
                                            if(debug.get())
                                                System.out.println("Username set as: " + codeData + " for user @ " + clientSocket.getLocalSocketAddress().toString());
                                            break;      
                                }
                            }
                            else{
                                if(debug.get()){
                                    System.out.println("Invalid Option Code Received");
                                }
                                outToClient.writeBytes("SVR:RPLY:ERR-INVALID\n");
                            }
                    }
                    else{
                        if(debug.get()){
                                    System.out.println("Invalid Option Code Received");
                                }
                        outToClient.writeBytes("SVR:RPLY:ERR-INVALID\n");
                    }
                            
                }
                //End option code handling
                else{
                    threads.send(userName+": "+input, this.getId());
                }
            }
            threads.removeThread(this.getId());
            if(debug.get()){
                System.out.println("Thread ID: "+this.getId()+" Killed");
            }
            clientSocket.close();
        } catch (IOException ex) {
            if(java.net.SocketException.class == ex.getClass()){
                if(debug.get()){
                    System.out.println(ex.getMessage());
                }
                threads.removeThread(this.getId());
            }
        }
    }
/**
     * This is an internal use method for the ClientThread class to encapsulate changing a String into an integer that can be used with a switch statement
     * @deprecated 
     * Java 1.7 allows for string-based switch statements making this code obsolete
     * @param requestCode The array to use to determine the opcode: options are "SET" or REQ"
     * @param type The string being compared to the array of codes in the requestCode array
     * @return the integer version of the opcode
     * 
     */
    private int optionParse(String type, String requestCode) {
        int optionID = 0;
        String[] table = null;
        if(type.equalsIgnoreCase("REQ")){
            table = reqIDtable;
        }
        if(type.equalsIgnoreCase("SET")){
            table = setIDtable;
        }
        if(table != null){
            for(int i = 0; i<table.length; i++){
                if(requestCode.equalsIgnoreCase(table[i])){
                    optionID = i+1;
                }
            }
        }
        return optionID;
    }
    /**
     * Method to get the socket attached to the Thread
     * @return The socket connected to this thread
     */
    public Socket getSocket(){
        return clientSocket;
    }
    /**
     * Used to send a method to the client attached to this Thread
     * @param message 
     */
    public void send(String message){
        try {
            if(debug.get()){
                System.out.println("Message sent to: " + this.getId());
            }
            outToClient.writeBytes(message+"\n");
        } catch (IOException ex) {
            if(debug.get()){
                System.out.println(ex.toString());
            }
        }
    }
    /**
     * Used to forcefully close a Thread
     */
    public void killSocket(){
        try {
            clientSocket.close();
        } catch (IOException ex) {
            if(debug.get()){
                System.out.println(ex.toString());
            }
        }
    }
}
